import { HTMLAttributes, PropType, computed, defineComponent, effect, nextTick, provide, reactive, ref, unref, watch, watchEffect, withCtx } from "vue";
export {default as MenuItem} from "./MenuItem";
export {default as SubMenu} from "./SubMenu";
export {default as MenuGroup} from "./MenuGroup";


export interface MenuProps extends HTMLAttributes {
    accordion?: boolean,
    theme?: 'light'|'dark',
    dir?: 'v'|'h',
    min?: boolean,
    activeName?: any,
    openKeys?: string[],
}

export const MenuContext = Symbol('MenuContext');
export const MenuTreeContext = Symbol('MenuTreeContext');

export default defineComponent({
    name: 'Menu',
    props: {
        accordion: {type: Boolean as PropType<MenuProps['accordion']>, default: false},
        theme: {type: String as PropType<MenuProps['theme']>, default: 'light'},
        dir: {type: String as PropType<MenuProps['dir']>, default: 'v'},
        min: {type: Boolean as PropType<MenuProps['min']>, default: false},
        activeName: {type: [String, Object] as PropType<MenuProps['activeName']>},
        openKeys: {type: Array as PropType<MenuProps['openKeys']>},
    },
    emits: ['select', 'update:activeName'],
    setup (props: MenuProps, { emit, slots }) {
        const accordion = () => props.accordion || false;
        const theme = () => props.theme || 'light';
        const dir = () => props.dir || 'v';
        const classList = computed(() => ({
            'cm-menu': true,
            [`cm-menu-${dir()}`]: dir(),
            [`cm-menu-min`]: props.min,
            [`cm-menu-${theme()}`]: theme(),
        }));
        const tree: any = [];
        const treeMap: any = {};

        const openKeys = ref({});

        watch(() => props.openKeys, (openedKeys) => {
            const map = {};
            openedKeys.forEach((key: string) => {
                map[key] = true;
            });
            openKeys.value = map;
        });

        watch(() => props.activeName, (name) => {
            store.activeName = name;
        });
        // 创建存储
        const store = reactive({
            activeName: props.activeName,
            min: props.min
        } as any);

        watch(() => store.activeName, (name) => {
            if (name) {
                // 父节点没打开的话进行打开
                // 页面刷新的时候初始化需要延迟等待treeMap加载数据
                updateParentOpenStatus(name);
            }
        });

        // 更新min参数的副作用
        watch(()=> props.min, (min) => {
            store.min = min;
        });

        // 更新父菜单的打开状态
        const updateParentOpenStatus = (name: string) => {
            let parent = treeMap && treeMap[name] && treeMap[name].parent;
            const oKs = unref(openKeys);
            if (parent) {
                while (parent) {
                    if (!oKs[parent.name]) {
                        setOpen(parent.name);
                    }
                    parent = parent.parent;
                }
            } else {
                // 顶级菜单项 横向的时候， 设置打开项 可以关闭已打开的subMenu
                if (dir() === 'h' || store.min) {
                    setOpen(name);
                }
            }
        };

        // 菜单点击处理
        const onSelect = (name: string, data: any) => {
            store.activeName = name;
            emit('update:activeName', name);
            emit('select', name, data);
        };

        // 子菜单是否存在已打开的
        const childrenOpen = (item: any, names: any) => {
            const oKs = unref(openKeys);
            item.children && item.children.forEach((aitem: any) => {
                if (oKs[aitem.name]) {
                    names[aitem.name] = true;
                }
                childrenOpen(aitem, names);
            });
        };

        // 设置打开
        const setOpen = (name: string) => {
            const oKs = unref(openKeys);
            if (accordion() || dir() === 'h' || store.min) {
                if (accordion() && oKs[name]) {
                    delete oKs[name];
                    openKeys.value = {...oKs};
                    return;
                }

                let item = treeMap[name];

                const names: any = {[name]: true};
                while (item.parent) {
                    names[item.parent.name] = true;
                    item = item.parent;
                }

                childrenOpen(item, names);

                const keys = Object.keys(oKs);
                keys.forEach((key: string) => {
                    if (!names[key]){
                        delete oKs[key];
                    }
                });
                openKeys.value = {...names};
            } else {
                if (oKs[name]) {
                    delete oKs[name];
                } else {
                    oKs[name] = true;
                }
                openKeys.value = {...oKs};
            }
        };

        provide(MenuContext, {padding: 16, onSelect, openKeys, store, setOpen, tree, treeMap, theme: theme(), dir: dir()});
        provide(MenuTreeContext, {padding: 0});

        return () => (
            <ul class={classList.value} x-padding={0} x-name="__root" x-level={0}>
                {slots.default && slots.default()}
            </ul>
        );
    }
});
